package main

import (
	"fmt"
	"math/rand"
	"time"
)

//习惯：channel遵循生产（返回值channel）消费（参数channel）模式，
//（不缓存阻塞）生产和消费其中一方需要goroutine，另一方就不能，一般生产方（返回channel，给你加数据）使用goroutine，消费方进行同步

// 生成器任务中断，优雅推出
func msgGen(s string, done chan struct{}) <-chan string {
	c := make(chan string)
	go func() {
		i := 0
		for {
			select {
			case <-time.After(time.Duration(rand.Intn(2000)) * time.Millisecond):
				c <- fmt.Sprintf("service %s，message %d", s, i)
			case <-done:
				fmt.Println("cheaning up!")
				time.Sleep(2 * time.Second)
				fmt.Println("cheaning done!")
				done <- struct{}{}
				return
			}
			i++

		}
	}()
	return c
}

// 等待多个服务，第一种写法
func fanIn(chs ...<-chan string) <-chan string {
	c := make(chan string)
	for _, ch := range chs { //ch进了一次{}，为循环全局变量，只有一个，值是最后一个
		// chCopy := ch //进了两次,局部变量
		// go func() {
		// 	for {
		// 		//c <- <-ch //循环变量坑！  循环变量生成gorutine，循环结束再被运行，一定小心循环变量进行拷贝
		// 		c <- <-chCopy
		// 	}
		// }()
		go func(in <-chan string) {
			for {

				c <- <-in
			}
		}(ch)
	}
	return c
}

// 等待多个服务，第二种写法,明确channel个数
func fanInBySelect(c1, c2 <-chan string) <-chan string {
	m := make(chan string)
	go func() {
		for {
			select {
			case n := <-c1:
				m <- n
			case n := <-c2:
				m <- n
			}
		}
	}()
	return m
}

// 非阻塞等待
func nonBlockingWait(c <-chan string) (string, bool) {
	select {
	case m := <-c:
		return m, true
	default:
		return "", false
	}
}

// 超时机制
func timeoutWait(c <-chan string, timeout time.Duration) (string, bool) {
	select {
	case m := <-c:
		return m, true
	case <-time.After(timeout):
		return "", false
	}
}

func main() {
	done := make(chan struct{})
	//channel可以看成服务的句柄
	m1 := msgGen("service1", nil) //消息实例  服务/任务
	m2 := msgGen("service2", done)
	m := fanIn(m1, m2)
	// m := fanInBySelect(m1, m2)
	for i := 0; i < 5; i++ {
		fmt.Println(<-m)
		if n, ok := nonBlockingWait(m2); ok {
			fmt.Println(n)
		} else {
			fmt.Println("nonBlockingWait")
		}
		if n, ok := timeoutWait(m2, 50*time.Millisecond); ok {
			fmt.Println(n)
		} else {
			fmt.Println("timeout")
		}
	}
	//取消任务
	done <- struct{}{} //初始化匿名结构体
	//优雅退出
	<-done
}
